package com.miyake.dsp.common;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import com.miyake.dsp.ComplexArray;
import com.miyake.dsp.converter.AbsoluteImplScilab;
import com.miyake.dsp.converter.DiffImplScilab;
import com.miyake.dsp.converter.MovingAverageScilab;
import com.miyake.dsp.converter.MovingAverageSetting;

public class CommonTool {
	public static int getPeakIndex(double[] data) {
		return getPeakIndex(data, 0, data.length-1);
	}

	public static double getAverage(double[] d, int start, int end) {
		double avg = 0;
		for (int i = start; i < end; i++) {
			avg += d[i];
		}
		return avg / (double)d.length;
	}
	public static double getAverage(double[] d) {
		return getAverage(d, 0, d.length - 1);
	}

	public static double getMaxValue(double[] v, int start, int end) {
		double max = -Double.MAX_VALUE;
		if (end > v.length-1) {
			end = v.length-1;
		}
		for (int i = start; i < end; i++) {
			if (max < v[i]) {
				max = v[i];
			}
		}
		return max;
	}

	public static double getMinValue(double[] v, int start, int end) {
		double min = Double.MAX_VALUE;
		for (int i = start; i < end; i++) {
			if (min > v[i]) {
				min = v[i];
			}
		}
		return min;
	}

	public static double getMaxValue(double[] data) {
		return getMaxValue(data, 0, data.length-1);
	}

	public static double getMinValue(double[] data) {
		return getMinValue(data, 0, data.length-1);
	}

	public static double[] getSmoothed(double[] data, int length) {
		MovingAverageScilab ma = new MovingAverageScilab(new MovingAverageSetting(length));
		ma.setSource(new ComplexArray(data));
		return ma.getProcessed().getRealPart();
	}

	public static double[] getDiff(double[] y) {
		DiffImplScilab diff = new DiffImplScilab();
		diff.setSource(new ComplexArray(y));
		return diff.getProcessed().getRealPart();
	}

	public static double[] getAbsolute(double[] filteredPulse) {
		AbsoluteImplScilab abs = new AbsoluteImplScilab();
		abs.setSource(new ComplexArray(filteredPulse));
		return abs.getProcessed().getRealPart();
	}

	public static double[] toPower(double[] v, double k) {
		double[] ret = new double[v.length];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = Math.pow(v[i], 2.0) * k;
		}
		return ret;
	}

	public static double[] getLimited(double[] in, double ceiling) {
		double[] ret = new double[in.length];
		for (int i = 0; i < in.length; i++) {
			if (in[i] > ceiling) {
				ret[i] = ceiling;
			}
			else {
				ret[i] = in[i];
			}
		}
		return ret;
	}

	public static double[] getGained(double[] in, double gain) {
		double[] ret = new double[in.length];
		for (int i = 0; i < in.length; i++) {
			ret[i] = in[i] * Math.pow(10.0, gain/10.0);
		}
		return ret;
	}

	public static double[] getWhiteNoise(int length, double magnitude, double dc) {
		double[] ret = new double[length];
		for (int i = 0; i < length; i++) {
			ret[i] = (Math.random()-0.5) * magnitude + dc;
		}
		return ret;
	}

	public static double[] getMixed(double[] out, double[] noise) {
		double[] ret = new double[out.length];
		for (int i = 0; i < out.length; i++) {
			ret[i] = out[i] + noise[i];
		}
		return ret;
	}

	public static double[] getOffseted(double[] y, double offset) {
		double[] ret = new double[y.length];
		for (int i = 0; i < y.length; i++) {
			ret[i] = y[i] + offset;
		}
		return ret;
	}

	public static double[] getZoneAverage(double[] data, int divisions) {
		double[] ret = new double[divisions];
		int zone = data.length / divisions;
		for (int i = 0; i < divisions; i++) {
			for (int j = i * zone; j < (i+1) * zone; j++) {
				ret[i] += data[j];
			}
			ret[i] /= zone;
		}
		return ret;
	}

	public static double getIntegral(double[] g) {
		double ret = 0;
		for (int i = 0; i < g.length; i++) {
			ret += g[i];
		}
		return ret;
	}

	public static double[] getMedianFiltered(double[] in, int length) {
		double[] ret = new double[in.length - length];
		for (int i = 0; i < ret.length; i++) {
			double[] subArray = ArrayUtils.subarray(in, i, i + length);
			Arrays.sort(subArray);
			ret[i] = subArray[subArray.length/2];
		}
		return ret;
	}

	public static double[] subtract(double[] out, double[] in2) {
		double[] ret = new double[out.length];
		for (int i = 0; i < out.length; i++) {
			ret[i] = out[i] - in2[i];
		}
		return ret;
	}

	public static double getInterpolated(double[] xarr, double[] yarr, double x) {
		double[] xx = new double[2];
		double[] yy = new double[2];

		xx[0] = xarr[0];
		yy[0] = yarr[0];
		for (int i = 0; i < xarr.length; i++) {
			if (xarr[i] > x) {
				xx[1] = xarr[i];
				yy[1] = yarr[i];
				break;
			}
			xx[0] = xarr[i];
			yy[0] = yarr[i];
		}
//		Fitting2 f = new Fitting2("y=x(1) + x(2)*t");
//		f.setOriginalData(xx, yy);
//		return f.getAt(x);
		return (yy[1] - yy[0]) / (xx[1] - xx[0]) * (x - xx[0]) + yy[0];
	}

	public enum RoundType {
		Round, Ceil, Floor
	}
	public static int toIndex(double[] x, double v, RoundType type) {
		double sres = x[1] - x[0];
		if (type.equals(RoundType.Round)) {
			return (int)Math.round((v - x[0]) / sres);
		}
		else if (type.equals(RoundType.Ceil)) {
			return (int)Math.ceil((v - x[0]) / sres);
		}
		else if (type.equals(RoundType.Floor)) {
			return (int)Math.floor((v - x[0]) / sres);
		}
		return 0;
	}

	public static double[] getLimitedOver(double[] lap, double d) {
		double[] ret = new double[lap.length];
		for (int i = 0; i < lap.length; i++) {
			if (lap[i] > 0.0) {
				ret[i] = lap[i];
			}
		}
		return ret;
	}

	public static double[] fillNumber(int length) {
		double[] ret = new double[length];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = i;
		}
		return ret;
	}

	public static boolean containsLargerValue(double[] data, double d) {
		for (int i = 0; i < data.length; i++) {
			if (data[i] >= d) {
				return true;
			}
		}
		return false;
	}

	public static Integer getPeakIndex(double[] data, int iStart, int iEnd) {
		double max = -Double.MAX_VALUE;
		int index = 0;
		for (int i = iStart; i <= iEnd; i++) {
			if (max < data[i]) {
				max = data[i];
				index = i;
			}
		}
		return index;
	}

	public static double[] getFloor(double[] smo, Double noiseLevel) {
		double[] ret = new double[smo.length];
		for (int i = 0; i < smo.length; i++) {
			if (smo[i] < noiseLevel) {
				ret[i] = noiseLevel;
			}
			else {
				ret[i] = smo[i];
			}
		}
		return ret;
	}

	public static int getMaxSpan(List<Integer> numbers) {
		int maxSpan = 0;
		int[] array = ArrayUtils.toPrimitive(numbers.toArray(new Integer[0]));
		for (int i = 1; i < array.length; i++) {
			if (array[i] - array[i-1] > maxSpan) {
				maxSpan = array[i] - array[i-1];
			}
		}
		return maxSpan;
	}

	public static int getMaxValue(int[] widthArray) {
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < widthArray.length; i++) {
			if (widthArray[i] > max) {
				max = widthArray[i];
			}
		}
		return max;
	}

	public static int getMinValue(int[] widthArray) {
		int min = Integer.MAX_VALUE;
		for (int i = 0; i < widthArray.length; i++) {
			if (widthArray[i] < min) {
				min = widthArray[i];
			}
		}
		return min;
	}

	public static int getApproximateCenterIndex( double data[], double center ) {
		int index = 0, i;
		for ( i = 0; i < data.length; i++ ) {
			if ( center <= data[ i ] ) {
				index = i;
				break;
			}
		}

		if ( i != 0 ) {
			double left = center - data[ index - 1 ];
			double right = data[ index ] - center;
			if ( left < right ) {
				index -= 1;
			}
		}

		return index;
	}

	public static int getPeakFromArea( double out[], int left, int right ) {
		int index = 0, i;
		double currentOut =  -90;

		for ( i = left; i <= right; i++ ) {
			if ( currentOut <= out[ i ] ) {
				index = i;
				currentOut = out[ i ];
			}
		}

		return index;
	}
}
